﻿using Nest;
using System.Collections.Concurrent;
using System.Text;
using XYS.Core.Models;
using XYS.Core.Util;

namespace XYS.Service.Core.Util.Helper
{
    /// <summary>
    /// 日志帮助类
    /// </summary>
    public static class LogHelper
    {
        /// <summary>
        /// 队列
        /// </summary>
        private readonly static ConcurrentQueue<HttpLoggerInfo> _queES = new();
        /// <summary>
        /// 队列
        /// </summary>
        private readonly static ConcurrentQueue<KeyValuePair<string, string>> _queFile = new();



        static LogHelper()
        {
            Init();
        }

        /// <summary>
        /// 新的日志进入队列
        /// </summary>
        /// <param name="path">日志目录（为空则默认目录：logs）</param>
        /// <param name="content">日志内容</param>
        public static void AddLog(string path, string content)
        {
            //压栈
            _queFile.Enqueue(new KeyValuePair<string, string>(path, content));
        }

        /// <summary>
        /// 新的日志进入队列
        /// </summary>
        public static void AddLog2Channel(HttpLoggerInfo httpLoggerInfo)
        {
            if (LogHelperSetting.ESClient != null)
            {
                _queES.Enqueue(httpLoggerInfo);
            }
        }

        /// <summary>
        /// 初始
        /// </summary>
        private static void Init()
        {
            //建立后台线程
            new Thread(() =>
            {
                while (true)
                {
                    try
                    {
                        //检查队列
                        if (!_queFile.IsEmpty)
                        {
                            //出栈
                            while (_queFile.TryDequeue(out var item))
                            {
                                try { SaveLog2File(item); } catch { }
                            }
                        }
                    }
                    finally
                    {
                        Thread.Sleep(1000);
                    }
                }
            })
            { IsBackground = true }.Start();

            new Thread(async () =>
            {
                while (true)
                {
                    //临时队列
                    ConcurrentQueue<HttpLoggerInfo> _queTemp = new();
                    if (_queES.Count > 1000)
                        Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] HTTP 日志积压[{_queES.Count}]");

                    //出栈，最多出1000条
                    for (int i = 0; i < 1000 && !_queES.IsEmpty; i++)
                    {
                        if (_queES.TryDequeue(out var item))
                        {
                            //压入临时栈
                            _queTemp.Enqueue(item);
                        }
                    }
                    try { await SaveLog2Channel(_queTemp.ToList()); }
                    catch { }
                    finally { Thread.Sleep(100); }
                }
            })
            { IsBackground = true }.Start();

        }


        /// <summary>
        /// 保存日志 -> 文件
        /// </summary>
        private static void SaveLog2File(string path, string content)
        {
            SaveLog2File(new KeyValuePair<string, string>(path, content));
        }
        /// <summary>
        /// 保存日志 -> 文件
        /// </summary>
        /// <param name="map"></param>
        private static void SaveLog2File(KeyValuePair<string, string> map)
        {
            string dirPath = "";
            string filePath = "";
            if (!string.IsNullOrWhiteSpace(map.Key))
            {
                string fileName = map.Key.TrimStart('/').TrimStart('\\');

                if (Path.GetFileName(fileName).Any(e => e == '.'))
                {
                    //包含文件名字
                    filePath = $"logs/http/{DateTime.Now:yyyy-MM-dd}/{fileName}";
                }
                else
                {
                    filePath = $"logs/http/{DateTime.Now:yyyy-MM-dd}/{fileName}.log";
                }
            }
            else
            {
                filePath = $"logs/http/{DateTime.Now:yyyy-MM-dd}/log.log";
            }
            dirPath = Path.GetDirectoryName(filePath);

            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
            if (!File.Exists(filePath))
            {
                var fs = File.Create(filePath);
                fs.Close();
            }
            File.AppendAllText(filePath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]{map.Value}\r\n");
        }
        /// <summary>
        /// 保存日志 -> 文件
        /// </summary>
        /// <param name="map"></param>
        private static void SaveLog2File(KeyValuePair<string, List<HttpLoggerInfo>> map) => SaveLog2File(new KeyValuePair<string, List<string>>(map.Key, map.Value.Select(x => x.ToJson()).ToList()));
        /// <summary>
        /// 保存日志 -> 文件
        /// </summary>
        /// <param name="map"></param>
        private static void SaveLog2File(KeyValuePair<string, List<string>> map)
        {
            string dirPath = "";
            string filePath = "";
            if (!string.IsNullOrWhiteSpace(map.Key))
            {
                string fileName = map.Key.TrimStart('/').TrimStart('\\');

                if (Path.GetFileName(fileName).Any(e => e == '.'))
                {
                    //包含文件名字
                    filePath = $"logs/http/{DateTime.Now:yyyy-MM-dd}/{fileName}";
                }
                else
                {
                    filePath = $"logs/http/{DateTime.Now:yyyy-MM-dd}/{fileName}.log";
                }
            }
            else
            {
                filePath = $"logs/http/{DateTime.Now:yyyy-MM-dd}/log.log";
            }
            dirPath = Path.GetDirectoryName(filePath);

            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
            if (!File.Exists(filePath))
            {
                var fs = File.Create(filePath);
                fs.Close();
            }

            StringBuilder sb = new();
            foreach (var item in map.Value)
            {
                sb.AppendLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]{item}");
            }
            File.AppendAllText(filePath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]{sb}");
        }


        /// <summary>
        /// 保存日志 -> 日志通道
        /// </summary>
        /// <param name="httpLoggerInfos"></param>
        private static async Task SaveLog2Channel(List<HttpLoggerInfo> httpLoggerInfos)
        {
            if (httpLoggerInfos == null || httpLoggerInfos.Count <= 0) return;

            //处理日志（响应数据）
            foreach (var httpLoggerInfo in httpLoggerInfos.Where(x => !string.IsNullOrWhiteSpace(x.ResponseBody) && x.ResponseBody.StartsWith("{")))
            {
                try
                {
                    //去除转义
                    var data = httpLoggerInfo.ResponseBody.Replace("\\\"", "\"").Trim('\"').ToObject<Dictionary<string, object>>();
                    if (data != null)
                    {
                        httpLoggerInfo.ResponseBodyMap = new List<KeyValueDTO<string, string>>();
                        foreach (var item in data.Keys)
                        {
                            try
                            {
                                var val = httpLoggerInfo.ResponseBodyMap.FirstOrDefault(x => x.Key == item);
                                if (val != null)
                                {
                                    val.Value += "," + data[item].ToJson()?.Trim('\"');
                                }
                                else
                                {
                                    val = new KeyValueDTO<string, string>
                                    {
                                        Key = item,
                                        Value = data[item].ToJson()?.Trim('\"')
                                    };
                                    httpLoggerInfo.ResponseBodyMap.Add(val);
                                }
                            }
                            catch { }
                        }
                    }
                }
                catch { }
            }

            switch (LogHelperSetting.LoggerChannelType)
            {
                case EnumAPILoggerChannelType.File:

                    var maps = httpLoggerInfos.GroupBy(x => $"{x.ApiHost}/{x.ApiPath.Replace("/", "-")}")
                        .ToDictionary(x => x.Key, x => x.Select(a => a.ToJson()).ToList());

                    foreach (var item in maps)
                    {
                        SaveLog2File(new KeyValuePair<string, List<string>>(item.Key, item.Value));
                    }
                    break;

                case EnumAPILoggerChannelType.ElasticSearch:
                    if (LogHelperSetting.ESClient != null && !string.IsNullOrWhiteSpace(LogHelperSetting.DefaultIndex))
                    {
                        List<IBulkOperation> bulkOperations = httpLoggerInfos.Select(x => new BulkIndexOperation<HttpLoggerInfo>(x) as IBulkOperation).ToList();
                        var result = await LogHelperSetting.ESClient.BulkAsync(new BulkRequest($"{LogHelperSetting.DefaultIndex}-{DateTime.Now:yyyy.MM.dd}")
                        {
                            Operations = bulkOperations
                        });
                    }
                    break;
                case EnumAPILoggerChannelType.Kafka:
                case EnumAPILoggerChannelType.RabbitMQ:
                case EnumAPILoggerChannelType.Unknown:
                default:
                    break;
            }

        }

    }
}
